/*
 * iap.c
 *
 *  Created on: 13-02-2012
 *      Author: Mateusz
 */

#include "LPC13xx.h"
#include <cr_section_macros.h>

#include "iap.h"

volatile uint8_t pi=0;

__AFTER_VECTORS const uint8_t pass_start[64] = {4,13,17,7,142,147,73,157,232,122,80,108,213,112,86,207,111,130,68,164,136,104,110,231,26,247,14,232,47,112,116,52,212,151,80,109,109,115,52,154,88,195,155,87,50,48,216,252,252,112,207,136,215,23,237,49,196,22,138,177,179,63,74,157};
volatile uint8_t pass[64];

__AFTER_VECTORS const unsigned sector_start_map[MAX_FLASH_SECTOR] = {SECTOR_0_START,             \
SECTOR_1_START,SECTOR_2_START,SECTOR_3_START,SECTOR_4_START,SECTOR_5_START,      \
SECTOR_6_START,SECTOR_7_START};

__AFTER_VECTORS const unsigned sector_end_map[MAX_FLASH_SECTOR] = {SECTOR_0_END,SECTOR_1_END,    \
SECTOR_2_END,SECTOR_3_END,SECTOR_4_END,SECTOR_5_END,SECTOR_6_END,SECTOR_7_END};

uint8_t user_flash_erased;

char flash_buf[FLASH_BUF_SIZE] __attribute__ ((aligned (8)));

unsigned * flash_address;
unsigned byte_ctr;

unsigned param_table[5];
unsigned result_table[4];

void iap_entry(unsigned param_tab[],unsigned result_tab[])
{
    void (*iap)(unsigned [],unsigned []);

    iap = (void (*)(unsigned [],unsigned []))IAP_ADDRESS;
    iap(param_tab,result_tab);
}

void erase_sector(unsigned start_sector,unsigned end_sector,unsigned cclk)
{
    param_table[0] = ERASE_SECTOR;
    param_table[1] = start_sector;
    param_table[2] = end_sector;
    param_table[3] = cclk;
    iap_entry(param_table,result_table);
}

void prepare_sector(unsigned start_sector,unsigned end_sector,unsigned cclk)
{
    param_table[0] = PREPARE_SECTOR_FOR_WRITE;
    param_table[1] = start_sector;
    param_table[2] = end_sector;
    param_table[3] = cclk;
    iap_entry(param_table,result_table);
}

void find_erase_prepare_sector(unsigned cclk, unsigned flash_address)
{
    unsigned i;

    __disable_irq();
    for(i=USER_START_SECTOR;i<=MAX_USER_SECTOR;i++)
    {
        if(flash_address < sector_end_map[i])
        {
            if( flash_address == sector_start_map[i])
            {
                prepare_sector(i,i,cclk);
                erase_sector(i,i,cclk);
            }
            prepare_sector(i,i,cclk);
            break;
        }
    }
    __enable_irq();
}

void write_data(unsigned cclk,unsigned flash_address,unsigned * flash_data_buf, unsigned count)
{
	__disable_irq();
    param_table[0] = COPY_RAM_TO_FLASH;
    param_table[1] = flash_address;
    param_table[2] = (unsigned)flash_data_buf;
    param_table[3] = count;
    param_table[4] = cclk;
    iap_entry(param_table,result_table);
    __enable_irq();
}

void erase_user_flash(void)
{
    prepare_sector(USER_START_SECTOR,MAX_USER_SECTOR,SystemCoreClock/1000);
    erase_sector(USER_START_SECTOR,MAX_USER_SECTOR,SystemCoreClock/1000);
	if(result_table[0] != CMD_SUCCESS)
    {
      while(1); /* No way to recover. Just let Windows report a write failure */
    }
}

int compute_binary_checksum(unsigned *pmem)
{

	unsigned checksum,i;

/*
 * The reserved Cortex-M3 exception vector location 7 (offset 0x001C
 * in the vector table) should contain the 2’s complement of the
 * checksum of table entries 0 through 6. This causes the checksum
 * of the first 8 table entries to be 0. This code checksums the
 * first 8 locations of the start of user flash. If the result is 0,
 * then the contents is deemed a 'valid' image.
 */
	checksum = 0;
	//pmem = (unsigned *)USER_FLASH_START;
	for (i = 0; i <= 7; i++) {
		checksum += *pmem;
		pmem++;
	}
	if (checksum != 0)
	{
		return (0);
	}
	else
	{
		return (1);
	}
}


char encoded = 0;

unsigned write_flash(unsigned * dst, char * src, unsigned no_of_bytes)
{
	unsigned i;

    if (flash_address == 0)
	{
	  /* Store flash start address */
	  flash_address = (unsigned *)dst;
	}

    if ((unsigned)dst == USER_FLASH_START)
    {
    	if (compute_binary_checksum((unsigned *)src))
    		encoded = 0;
    	else
			encoded = 1;
    }

    if (encoded)
    {
		for( i = 0;i<no_of_bytes;i++ )
		{
		  flash_buf[(byte_ctr+i)] = ((*(src+i)) ^ pass[pi&0x3F]) ;
		  pass[pi & 0x3F] = (flash_buf[(byte_ctr+i)] ^ pass[pi & 0x3F]);
		  pi++;
		}
    }
    else
    {
    	for (i = 0; i<no_of_bytes;i++)
    	{
    		flash_buf[(byte_ctr+i)] = ((*(src+i)));
    	}
    }
	byte_ctr = byte_ctr + no_of_bytes;

	if( byte_ctr == FLASH_BUF_SIZE)
	{
	  /* We have accumulated enough bytes to trigger a flash write */
	  find_erase_prepare_sector(SystemCoreClock/1000, (unsigned)flash_address);
      if(result_table[0] != CMD_SUCCESS)
      {
        while(1); /* No way to recover. Just let Windows report a write failure */
      }
      write_data(SystemCoreClock/1000,(unsigned)flash_address,(unsigned *)flash_buf,FLASH_BUF_SIZE);
      if(result_table[0] != CMD_SUCCESS)
      {
        while(1); /* No way to recover. Just let Windows report a write failure */
      }
	  /* Reset byte counter and flash address */
	  byte_ctr = 0;
	  flash_address = 0;
	}
    return(CMD_SUCCESS);
}

int user_code_present(void)
{
    param_table[0] = BLANK_CHECK_SECTOR;
    param_table[1] = USER_START_SECTOR;
    param_table[2] = USER_START_SECTOR;
    iap_entry(param_table,result_table);
	if( result_table[0] == CMD_SUCCESS )	//pusty sektor
	{
		return 0;
	}

	return compute_binary_checksum((unsigned *)USER_FLASH_START);

	return 1;
}
